package ElasticSearch

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	contentMap = sync.Map{}     //数据库
	contentMu sync.Mutex        //数据库锁定防止并发访问

	repositoryMap = sync.Map{}  //仓储
	repositoryMu = sync.Mutex{} //仓储锁定防止并发访问
)

//DataBaseMapping 添加映射
// @client 连接对象
func DataBaseMapping( config map[string]string) (*ElasticContent,error) {
	ipport := config["ipport"]
	dbname := config["dbname"]
	key := fmt.Sprint(ipport, "@#@", dbname)
	value, ok := contentMap.Load(key)
	if ok {
		return value.(*ElasticContent), nil
	}
	contentMu.Lock()
	defer contentMu.Unlock()

	value, ok = contentMap.Load(key)
	if ok {
		return value.(*ElasticContent), nil
	}

	timeout := time.Second * 30
	if timeoutStr, ok := config["timeout"]; ok {
		atoi, err := strconv.Atoi(timeoutStr)
		if err == nil {
			timeout = time.Second * time.Duration(atoi)
		}
	}
	// 获取mongo实例连接
	client, err := GetElasticClient(config)
	if err != nil {
		return nil, err
	}
	content := &ElasticContent{client, timeout}
	contentMap.Store(key, content)
	return content, nil
}

//ElasticContent 集合上下文
type ElasticContent struct {
	*ElasticClient
	timeout  time.Duration
}

//GetElasticRepository 获取集合仓储
// entity 结构体
func (elasti *ElasticContent) GetElasticRepository(entity interface{}) IElasticRepository {
	var entityName string
	typeOfCat := reflect.TypeOf(entity)
	typ := typeOfCat
	if typeOfCat.Kind() == reflect.Ptr {
		entityName = typeOfCat.Elem().Name()
		typ = typ.Elem()
	} else {
		entityName = typeOfCat.Name()
	}
	var key = fmt.Sprint(elasti.index, "@#@", entityName)
	val, ok := repositoryMap.Load(key)
	if ok {
		return val.(IElasticRepository)
	}
	repositoryMu.Lock()
	defer repositoryMu.Unlock()

	val, ok = repositoryMap.Load(key)
	if ok {
		return val.(IElasticRepository)
	}
	repository :=  &ElasticRepository{
		content:   elasti,
		tableName: strings.ToLower(entityName),
		typ:       typ,
		timeout:   elasti.timeout,
	}
	repositoryMap.Store(key,repository)
	return repository
}
